#
# Copyright 2009 Red Hat, Inc. and/or its affiliates.
#
# Licensed to you under the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.  See the files README and
# LICENSE_GPL_v2 which accompany this distribution.
#
"""
Cache module provides general purpose (more or less) cache infrastructure
for keeping storage related data that is expensive to harvest, but needed often
"""

import logging
import constants
import misc
import storage_exception as se
import cache
import lvm

class LVMCache:
    """
    LVMCache stores info on lvm objects' status
    """
    __cache = cache.Cache()
    log = logging.getLogger("irs")

    @classmethod
    def flush(cls):
        """
        Flush the LVM cache
        """
        cls.__cache.flush()

    @classmethod
    def addVG(cls, vg):
        """
        """
        cls.__cache.add(vg.name, vg)

    @classmethod
    def removeVG(cls, vg):
        """
        """
        cls.__cache.remove(vg.name)

    @classmethod
    def lookup(cls, vgname):
        try:
            vgdata = cls.__cache.lookup(vgname)
        except:
            vgdata = None

        if not vgdata or vgdata['stale']:
            cls.refresh()
            vgdata = cls.__cache.lookup(vgname)
        return vgdata

    @classmethod
    def markStaleVg(cls, vgname):
        try:
            vgdata = cls.__cache.lookup(vgname)
            vgdata['stale'] = True
        except:
            pass

    @classmethod
    def getall(cls):
        return cls.__cache.getvalues()

    @classmethod
    def getVGNames(cls):
        return cls.__cache.getkeys()

    @classmethod
    def getLV(cls, vgname, lvname):
        try:
            vgdata = cls.lookup(vgname)
            return vgdata['lvs'][lvname]
        except:
            raise se.GetLogicalVolumeDataError([vgname,lvname])


    @classmethod
    def refresh(cls):
        """
        Refresh the lvm info
        """
        cmd = [constants.EXT_VGS, "--noheadings", "-o", "vg_name,vg_uuid,attr,vg_size,vg_free,vg_extent_size,vg_extent_count,tags", "--nosuffix", "--units", "b"]
        cmd = lvm.addExtraCfg(cmd)
        (rc, vgout, err) = misc.execCmd(cmd)
        if rc != 0:
            raise se.GetVolumeGroupListError()

        cmd = [constants.EXT_LVS, "--noheadings", "-o", "vg_name,name,attr,lv_size,devices,tags", "--units", "b", "--nosuffix"]
        cmd = lvm.addExtraCfg(cmd)
        (rc, lvout, err) = misc.execCmd(cmd)
        if rc != 0:
            raise se.CouldNotRetrieveLogicalVolumesList()

        cmd = [constants.EXT_PVS, "--noheadings", "-o",
            "vg_name,vg_uuid,pv_name,pv_uuid,dev_size,pv_pe_count,pv_pe_alloc_count",
            "--units", "b", "--nosuffix"]
        cmd = lvm.addExtraCfg(cmd)
        (rc, pvout, err) = misc.execCmd(cmd)
        if rc != 0:
            raise se.CouldNotRetrievePhysicalVolumeList("error code: %s" % rc)

        cls.flush()
        vgs = {}
        for vg in vgout:
            (name,uuid,attr,size,free,extent_size,extent_count,tags) = ("","","","","","","","")
            vg = vg.split()
            try:
                if len(vg) > 8:
                    raise  # something wrong
                (name,uuid,attr,size,free,extent_size,extent_count) = vg[:7]
                if len(vg) == 8:
                    tags = vg[7]
            except Exception:
                cls.log.warning("ignoring vgs line: %s" % (" ".join(vg)))
                continue

            vgs[name] = {'uuid':uuid, 'size':size, 'attr':attr, 'free':free, 'extent_size':extent_size,
                            'extent_count':extent_count, 'tags':tags.split(","), 'lvs':{},
                            'pvs':{}, 'stale':False}

        for lv in lvout:
            (vg,name,attr,size,devices,tags) = ("","","","","","")
            lv = lv.split()
            try:
                if len(lv) > 6:
                    raise  # something wrong
                (vg,name,attr,size,devices) = lv[:5]
                if len(lv) == 6:
                    tags = lv[5]
            except Exception:
                cls.log.warning("ignoring lvs line: %s" % (" ".join(lv)))
                continue
            # Sanity check
            if vg not in vgs:
                cls.log.warning("Error in parsing lvs, vg not found in cache.  offending line: %s" % (" ".join(lv)))
                continue

            # split device name and extent start
            devices = devices.strip(" )").split("(")

            # If lv has several extents then we only need to update the devices list
            if name in vgs[vg]['lvs']:
                vgs[vg]['lvs'][name]['devices'].append(devices)
            # First extent is always the first one listed and is inserted as the first element of devices
            else:
                vgs[vg]['lvs'][name] = {'attr':attr, 'size':size, 'devices':[devices], 'tags':tags.split(",")}

        for pv in pvout:
            pv = pv.split()
            if len(pv) != 7:
                cls.log.warning("ignoring pvs line: %s" % (" ".join(pv)))
                continue
            (vg,vg_uuid,name,uuid,size,pe_count,pe_alloc_count) = pv
            if vg not in vgs:
                cls.log.warning("Error in parsing pvs, vg not found in cache.  offending line: %s" % (" ".join(pv)))
                continue
            vgs[vg]['pvs'][name] = {'uuid':uuid, 'vg_uuid':vg_uuid, 'size':size,
                                     'pe_count':pe_count, 'pe_alloc_count':pe_alloc_count}

        cls.__cache.setall(vgs)


